//
//  MFAPMUploadManager.m
//  MFAPMManager
//
//  Created by zhang liyu on 2019/6/3.
//

#import "MFAPMUploadManager.h"

#import "MFAPMRequestManager.h"
#import "MFAPMDataFactory.h"
#import "MFAPMBaseModel.h"
#import "MFAPMDBManager.h"
#import "MFAPMManager.h"
#import "MFAPMUtility.h"
#import "MFAPMConfig.h"
#import "MFAPMDefine.h"

#import <pthread.h>

@interface MFAPMUploadManager ()

@property (nonatomic, strong) NSOperationQueue *memoryCacheQueue;
@property (nonatomic, strong) NSOperationQueue *uploadCacheQueue;

@property (nonatomic, strong) NSMutableArray<NSDictionary *> *memoryCacheItemsArr;
@property (nonatomic, strong) NSMutableDictionary<NSNumber *,NSArray *> *uploadCahcheItemsDict;

@property (nonatomic, assign) pthread_mutex_t lock;
@property (nonatomic, assign) BOOL isStoreItemsToDisk;

@end

@implementation MFAPMUploadManager

#pragma mark - Singleton

+ (instancetype)sharedInstance{
    static MFAPMUploadManager *uploadManager = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        uploadManager = [[MFAPMUploadManager alloc] init];
    });
    return uploadManager;
}

#pragma mark - Life Circle

- (instancetype)init{
    self = [super init];
    if(self){
        [self installActions];
    }
    return self;
}

- (void)installActions{
    pthread_mutex_init(&_lock, NULL);
}

#pragma mark - Item Cache Actions

+ (void)cacheItem:(MFAPMBaseModel *)item{
    [[self class] cacheItem:item AndStoreToDiskNow:NO];
}

+ (void)cacheItem:(nullable MFAPMBaseModel *)item AndStoreToDiskNow:(BOOL)isStoreToDiskNow{
    
    if(!isStoreToDiskNow){
        //非立即存储事件，存储到内存，触发发送策略
        [[self class] storeToMemoryWithItem:item];
    }
    else{
        //立即存储事件，获取全部内存事件存储到数据库
        [[self class] storeToDiskWithItem:item];
    }
}

#pragma mark - Store Actions

+ (void)storeCacheItemsToDiskNow{
    //立即将内存中保存事件保存到数据库中
    [[self class] cacheItem:nil AndStoreToDiskNow:YES];
}

+ (void)storeToMemoryWithItem:(nullable MFAPMBaseModel *)item{
    if(![item isKindOfClass:[MFAPMBaseModel class]] || item.timestamp <= 0){
        return;
    }
    [[MFAPMUploadManager sharedInstance].memoryCacheQueue addOperationWithBlock:^{
        NSDictionary *modelParametersDict = [MFAPMDataFactory configParametersDictFromModel:item];
        if(modelParametersDict.count == 0){
            return;
        }
        [[MFAPMUploadManager sharedInstance].memoryCacheItemsArr addObject:modelParametersDict];
        
        NSUInteger itemsCount = [MFAPMUploadManager sharedInstance].memoryCacheItemsArr.count;
        //当进行内存数据保存时，不进行发送的触发
        //当内存数据达到触发条件，内存缓存事件条数限制时，进行事件发送处理
        if(![MFAPMUploadManager sharedInstance].isStoreItemsToDisk
           && itemsCount >= [MFAPMManager sharedManager].config.maxMemoryCacheNUM){
            //数量达到上报条件，进行数据上报
            [[self class] configUploadCacheItems:[NSArray arrayWithArray:[MFAPMUploadManager sharedInstance].memoryCacheItemsArr]];
            //清空内存缓存数组
            [[MFAPMUploadManager sharedInstance].memoryCacheItemsArr removeAllObjects];
        }
    }];
}

//将内存数据全部缓存到数据库中
//若model有值，则将其放到保存数组的最后一个
+ (void)storeToDiskWithItem:(nullable MFAPMBaseModel *)model{
    
    //设置状态标记位，避免重新触发发送
    [MFAPMUploadManager sharedInstance].isStoreItemsToDisk = YES;
    
    //取消上传队列中全部操作
    [[MFAPMUploadManager sharedInstance].uploadCacheQueue cancelAllOperations];
    
    [[MFAPMUploadManager sharedInstance].uploadCacheQueue addOperationWithBlock:^{
        
        //获取当前上传缓存队列中数据，并清空上传缓存队列
        __block NSMutableArray<NSDictionary *> *cacheItemsArr = [NSMutableArray array];
        [[MFAPMUploadManager sharedInstance].safeUploadCacheItemsDict.allValues enumerateObjectsUsingBlock:^(NSArray * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
            [cacheItemsArr addObjectsFromArray:obj];
        }];
        [[MFAPMUploadManager sharedInstance] removeUploadCacheItems];
        
        //内存事件队列中进行全部数据的保存操作
        [[MFAPMUploadManager sharedInstance].memoryCacheQueue addOperationWithBlock:^{
            
            //保存内存队列缓存事件
            [cacheItemsArr addObjectsFromArray:[MFAPMUploadManager sharedInstance].memoryCacheItemsArr];
            //清空内存缓存队列
            [[MFAPMUploadManager sharedInstance].memoryCacheItemsArr removeAllObjects];
            
            //保存新添加的事件
            if(model.timestamp > 0){
                NSDictionary *modelParametersDict = [MFAPMDataFactory configParametersDictFromModel:model];
                if(modelParametersDict.count > 0){
                    [cacheItemsArr addObject:modelParametersDict];
                }
            }
            //将内存和上传缓存中的数据保存到数据库
            [MFAPMDBManager storeDataItemsToDisk:[NSArray arrayWithArray:cacheItemsArr]];

            //重置状态标记位
            [MFAPMUploadManager sharedInstance].isStoreItemsToDisk = NO;
        }];
    }];
}

#pragma mark - Upload Actions

+ (void)uploadCacheDataNow{
    [[self class] uploadCacheDataNowWithItem:nil];
}

+ (void)uploadCacheDataNowWithItem:(nullable MFAPMBaseModel *)item{
    
    [[MFAPMUploadManager sharedInstance].memoryCacheQueue addOperationWithBlock:^{
        
        //若存在数据，则进行对应数据的保存操作
        NSDictionary *modelParametersDict = [MFAPMDataFactory configParametersDictFromModel:item];
        if(modelParametersDict.count > 0){
            [[MFAPMUploadManager sharedInstance].memoryCacheItemsArr addObject:modelParametersDict];
        }
        //当进行内存数据保存时，不进行发送的触发
        if(![MFAPMUploadManager sharedInstance].isStoreItemsToDisk){
            //进行数据上报
            [[self class] configUploadCacheItems:[NSArray arrayWithArray:[MFAPMUploadManager sharedInstance].memoryCacheItemsArr]];
            //清空内存缓存数组
            [[MFAPMUploadManager sharedInstance].memoryCacheItemsArr removeAllObjects];
        }
    }];
}

+ (void)configUploadCacheItems:(NSArray<NSDictionary *> *)itemsArr{
    //获取数据库缓存数据
    NSMutableArray *uploadItemsArr = [NSMutableArray arrayWithArray:[MFAPMDBManager fetchDiskAllDataItemsAndClearTable:YES]];
    //添加内存数据
    [uploadItemsArr addObjectsFromArray:itemsArr];
    
    //无上传数据时返回
    if(uploadItemsArr.count == 0){
        return;
    }
    
    //单次允许上传的最大长度
    NSInteger dividStepLength = [MFAPMManager sharedManager].config.maxReportCount;
    
    //不满足拆包条件或者步长为0时，直接上传
    if(dividStepLength == 0 || uploadItemsArr.count <= dividStepLength){
        [self uploadItemsArr:uploadItemsArr];
    }
    //对上传数据按照每次能够上传的最大数目进行拆包
    else{
        //拆包数组
        NSArray<NSArray *> *dealItemsArr = [MFAPMUtility dividSourceArr:uploadItemsArr byStepLength:dividStepLength];
        
        //对拆包数据进行上传操作
        for (NSArray *uploadArr in dealItemsArr) {
            if(![uploadArr isKindOfClass:[NSArray class]]
               || uploadArr.count == 0){
                continue;
            }
            [self uploadItemsArr:uploadArr];
        }
    }
}

+ (void)uploadItemsArr:(NSArray<NSDictionary *> *)uploadItemsArr{
    
    //保存当前的上传数据，以时间戳为key
    NSTimeInterval uploadItemsArrKey = [[NSDate date] timeIntervalSince1970];
    [[MFAPMUploadManager sharedInstance] setUploadItemsArr:uploadItemsArr forKey:uploadItemsArrKey];
    
    [[MFAPMUploadManager sharedInstance].uploadCacheQueue addOperationWithBlock:^{
        //上传uploadItemsArr数据，在成功后进行数据的清除操作，失败后进行上传数据缓存的判断
        [self sendItemsArr:uploadItemsArr forKey:uploadItemsArrKey];
    }];
}

+ (void)sendItemsArr:(NSArray<NSDictionary *> *)sendItemsArr forKey:( NSTimeInterval)uploadKey{
    
    //上传参数
    NSDictionary *parametersDict = [MFAPMDataFactory configUploadRequestParametersDictFromDataArr:sendItemsArr] ? : [NSDictionary dictionary];
    //上传数据
    [MFAPMRequestManager POST:MFAPM_REQUEST_URL_UPLOAD parametersDict:parametersDict success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        //请求成功
        [[MFAPMUploadManager sharedInstance].uploadCacheQueue addOperationWithBlock:^{
            //清除上传缓存中数据
            [[MFAPMUploadManager sharedInstance] removeItemsArrFromUploadDictForKey:uploadKey];
        }];
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        //请求失败，认为最近无法上传数据，进行缓存中数据的数据库保存操作，可能会造成数据重复
        //需要进行性能测试
        [[MFAPMUploadManager sharedInstance].uploadCacheQueue cancelAllOperations];
        [[MFAPMUploadManager sharedInstance].uploadCacheQueue addOperationWithBlock:^{
            //将当前上传缓存队列中数据保存到数据库，并清空上传缓存队列
            __block NSMutableArray<NSDictionary *> *cacheItemsArr = [NSMutableArray array];
            [[MFAPMUploadManager sharedInstance].safeUploadCacheItemsDict.allValues enumerateObjectsUsingBlock:^(NSArray * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
                [cacheItemsArr addObjectsFromArray:obj];
            }];
            [[MFAPMUploadManager sharedInstance] removeUploadCacheItems];
            
            //将上传缓存队列中数据保存到数据库，因为需要保留内存缓存队列触发下次上传，故暂时并不进行内存缓存事件的数据库保存操作
            [MFAPMDBManager storeDataItemsToDisk:cacheItemsArr];
        }];
    }];
}

#pragma mark - Setter & Getter

- (NSOperationQueue *)memoryCacheQueue{
    if(!_memoryCacheQueue){
        _memoryCacheQueue = [[NSOperationQueue alloc] init];
        _memoryCacheQueue.maxConcurrentOperationCount = 1;
    }
    return _memoryCacheQueue;
}

- (NSOperationQueue *)uploadCacheQueue{
    if(!_uploadCacheQueue){
        _uploadCacheQueue = [[NSOperationQueue alloc] init];
        _uploadCacheQueue.maxConcurrentOperationCount = 1;
    }
    return _uploadCacheQueue;
}

- (NSMutableArray<NSDictionary *> *)memoryCacheItemsArr{
    if(!_memoryCacheItemsArr){
        _memoryCacheItemsArr = (NSMutableArray<NSDictionary *> *)[NSMutableArray array];
    }
    return _memoryCacheItemsArr;
}

- (NSMutableDictionary<NSNumber *,NSArray *> *)uploadCahcheItemsDict{
    if(!_uploadCahcheItemsDict){
        _uploadCahcheItemsDict = (NSMutableDictionary<NSNumber *,NSArray *> *)[NSMutableDictionary dictionary];
    }
    return _uploadCahcheItemsDict;
}

- (NSMutableDictionary<NSString *,NSArray *> *)safeUploadCacheItemsDict{
    NSMutableDictionary<NSString *,NSArray *> *itemsDict;
    pthread_mutex_lock(&_lock);
    itemsDict = (NSMutableDictionary<NSString *,NSArray *> *)[NSMutableDictionary dictionaryWithDictionary:self.uploadCahcheItemsDict];
    pthread_mutex_unlock(&_lock);
    return itemsDict;
}

- (void)setUploadItemsArr:(NSArray<NSDictionary *> *)itemsArr forKey:(NSTimeInterval)key{
    pthread_mutex_lock(&_lock);
    [self.uploadCahcheItemsDict setObject:itemsArr forKey:@(key)];
    pthread_mutex_unlock(&_lock);
}

- (void)removeItemsArrFromUploadDictForKey:(NSTimeInterval)key{
    pthread_mutex_lock(&_lock);
    [self.uploadCahcheItemsDict removeObjectForKey:@(key)];
    pthread_mutex_unlock(&_lock);
}

- (void)removeUploadCacheItems{
    pthread_mutex_lock(&_lock);
    [self.uploadCahcheItemsDict removeAllObjects];
    pthread_mutex_unlock(&_lock);
}

@end
